// <copyright file="MainForm.cs" company="Sinclair Community College">
// Copyright (c) Sinclair Community College. All rights reserved.
// </copyright>

namespace SinclairCC.AppVSuite.DuplicateRootDetector
{
    using System;
    using System.ComponentModel;
    using System.Data;
    using System.Text;
    using System.Threading;
    using System.Windows.Forms;

    /// <summary>
    /// Defines the main form for the application.
    /// </summary>
    internal partial class MainForm : Form
    {
        /// <summary>
        /// A database to store SFT file information.
        /// </summary>
        private Database db;

        /// <summary>
        /// Initializes a new instance of the MainForm class.
        /// </summary>
        public MainForm()
        {
            this.InitializeComponent();

            // Restore the form's last size and location.
            this.Location = DuplicateRootDetector.Properties.Settings.Default.mainFormLocation;
            this.Size = DuplicateRootDetector.Properties.Settings.Default.mainFormSize;

#if DEBUG
            this.textFolderPath.Text = System.Environment.ExpandEnvironmentVariables("%USERPROFILE%");
#endif
        }
        
        /// <summary>
        /// Handles the Click event for the Browse button.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// Data specific to the event.
        /// </param>
        private void ClickBrowseForFolderButton(object sender, EventArgs e)
        {
            FolderBrowserDialog fbd = new FolderBrowserDialog();
            fbd.Description = "Select a folder to examine for duplicate root folders and package IDs.";
            fbd.RootFolder = Environment.SpecialFolder.DesktopDirectory;
            if (this.textFolderPath.Text.Trim().Length > 0)
            {
                fbd.SelectedPath = this.textFolderPath.Text.Trim();
            }

            fbd.ShowNewFolderButton = true;
            if (fbd.ShowDialog(this) == DialogResult.OK)
            {
                this.textFolderPath.Text = fbd.SelectedPath;
            }
        }

        /// <summary>
        /// Handles the Click event for the Check button.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// Data specific to the event.
        /// </param>
        private void ClickCheckButton(object sender, EventArgs e)
        {
            string folderPath = this.textFolderPath.Text.Trim();
            if (!System.IO.Directory.Exists(folderPath))
            {
                MessageBox.Show(this, string.Format("Folder \"{0}\" does not exist.", folderPath), "Folder Not Found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
            else
            {
                this.bgw.RunWorkerAsync(folderPath);
            }
        }
        
        /// <summary>
        /// Creates ListView items based on the file information database contents.
        /// </summary>
        private void CreateListViewItems()
        {
            this.duplicateListView.BeginUpdate();

            this.duplicateListView.Groups.Clear();
            this.duplicateListView.Items.Clear();

            foreach (string rootFolderName in this.db.DuplicatedRootFolderNames)
            {
                ListViewGroup newGroup = new ListViewGroup(string.Format("root folder: {0}", rootFolderName.ToLower(System.Globalization.CultureInfo.CurrentCulture)));
                this.duplicateListView.Groups.Add(newGroup);

                DataTable fileRows = this.db.GetSftInfoForRootFolder(rootFolderName);
                foreach (DataRow fileRow in fileRows.Rows)
                {
                    string tempString = fileRow["SftPath"].ToString();
                    System.Windows.Forms.ListViewItem newItem = new System.Windows.Forms.ListViewItem(tempString);

                    tempString = FileSize.AutoFormat((long)fileRow["Size"]);
                    newItem.SubItems.Add(tempString);

                    tempString = ((DateTime)fileRow["Created"]).ToString();
                    newItem.SubItems.Add(tempString);

                    tempString = ((DateTime)fileRow["LastModified"]).ToString();
                    newItem.SubItems.Add(tempString);

                    newItem.Group = newGroup;
                    this.duplicateListView.Items.Add(newItem);
                }
            }

            foreach (Guid packageId in this.db.DuplicatedPackageIds)
            {
                ListViewGroup newGroup = new ListViewGroup(string.Format("package ID: {0}", packageId.ToString("D").ToLower(System.Globalization.CultureInfo.CurrentCulture)));
                this.duplicateListView.Groups.Add(newGroup);

                DataTable fileRows = this.db.GetSftInfoForPackageId(packageId);
                foreach (DataRow fileRow in fileRows.Rows)
                {
                    string tempString = fileRow["SftPath"].ToString();
                    System.Windows.Forms.ListViewItem newItem = new System.Windows.Forms.ListViewItem(tempString);

                    tempString = FileSize.AutoFormat((long)fileRow["Size"]);
                    newItem.SubItems.Add(tempString);

                    tempString = ((DateTime)fileRow["Created"]).ToString();
                    newItem.SubItems.Add(tempString);

                    tempString = ((DateTime)fileRow["LastModified"]).ToString();
                    newItem.SubItems.Add(tempString);

                    newItem.Group = newGroup;
                    this.duplicateListView.Items.Add(newItem);
                }
            }

            this.duplicateListView.EndUpdate();
        }

        /// <summary>
        /// Populates and configures the file ListView.
        /// </summary>
        private void PrepareListViewItems()
        {
            this.statusLabel.Text = "Populating list...";

            this.CreateListViewItems();
            this.SizeListViewColumns();
            this.UpdateFileCounts();
            
            this.statusLabel.Text = "Ready.";
        }

        /// <summary>
        /// Handles the RunWorkerCompleted event for the BackgroundWorker.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// Data specific to the event.
        /// </param>
        private void BackgroundWorkCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.bgw.WorkerReportsProgress = false;

            this.PrepareListViewItems();
            
            System.Text.StringBuilder message = new StringBuilder();
            message.Append(string.Format("Processed {0:N0} .SFT files totalling {1}.", this.db.SftFileCount, FileSize.AutoFormat(this.db.TotalSftFileSize)));            
            message.Append(System.Environment.NewLine);

            int duplicateFolderCount = this.db.DuplicatedRootFolderNames.Length;
            int duplicatedPackageIdCount = this.db.DuplicatedPackageIds.Length;

            if (duplicateFolderCount == 0)
            {
                message.Append("No duplicated root folders were found.");
            }
            else
            {
                message.Append(string.Format("{0:N0}", duplicateFolderCount));
                message.Append(string.Format(" duplicated root folder{0} {1} found.", duplicateFolderCount == 1 ? string.Empty : "s", duplicateFolderCount == 1 ? "was" : "were"));
            }
            message.Append(System.Environment.NewLine);
            if (duplicatedPackageIdCount == 0)
            {
                message.Append("No duplicated package IDs were found.");
            }
            else
            {
                message.Append(string.Format("{0:N0}", duplicatedPackageIdCount));
                message.Append(string.Format(" duplicated package ID{0} {1} found.", duplicatedPackageIdCount == 1 ? string.Empty : "s", duplicatedPackageIdCount == 1 ? "was" : "were"));
            }

            MessageBox.Show(this, message.ToString(), "Duplicate Root Detector", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);

            if (this.db.HasErrors)
            {
                DialogResult showErrors = MessageBox.Show("The operation completed with errors. Would you like to see those errors?", "Duplicate Root Detector", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);
                if (showErrors == DialogResult.Yes)
                {
                    ErrorDisplayForm edf = new ErrorDisplayForm(this.db.ErrorTable);
                    edf.Show(this);
                }
            }           
        }

        /// <summary>
        /// Handles the DoWork event for the BackgroundWorker.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// Data specific to the event.
        /// </param>
        private void DoBackgroundWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker bgw = sender as BackgroundWorker;
            bgw.WorkerReportsProgress = true;
            string folderPath = (string)e.Argument;
            this.db = new Database();
            this.db.StatusChanged += new Database.StatusChangedEventHandler(this.DatabaseStatusChanged);
            this.db.SurveyDirectory(folderPath);
            e.Cancel = bgw.CancellationPending;
        }

        /// <summary>
        /// Updates the file count labels in the status bar.
        /// </summary>
        private void UpdateFileCounts()
        {
            if (null == this.db)
            {
                this.fileCountLabel.Text = "0 SFT files.";
                this.rootFolderCountLabel.Text = "0 duplicated root folders.";
                this.packageIdCountLabel.Text = "0 duplicated package IDs.";
            }
            else
            {
                int duplicateFolderCount = this.db.DuplicatedRootFolderNames.Length;
                int duplicatedPackageIdCount = this.db.DuplicatedPackageIds.Length;

                this.fileCountLabel.Text = string.Format("{0:N0} SFT file{1}.", this.duplicateListView.Items.Count, this.duplicateListView.Items.Count == 1 ? string.Empty : "s");
                this.rootFolderCountLabel.Text = string.Format("{0:N0} duplicated root folder{1}.", duplicateFolderCount, duplicateFolderCount == 1 ? string.Empty : "s");
                this.packageIdCountLabel.Text = string.Format("{0:N0} duplicated package ID{1}.", duplicatedPackageIdCount, duplicatedPackageIdCount == 1 ? string.Empty : "s");
            }
        }

        /// <summary>
        /// Handles the StatusChanged event for the database.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="newStatus">
        /// Data specific to the event.
        /// </param>
        private void DatabaseStatusChanged(object sender, SearchStatus newStatus)
        {
            if (this.bgw.WorkerReportsProgress)
            {
                this.bgw.ReportProgress(0, newStatus);
            }
        }
        
        /// <summary>
        /// Handles the ProgressChanged event for the BackgroundWorker.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// Data specific to the event.
        /// </param>
        private void BackgroundWorkProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            SearchStatus status = e.UserState as SearchStatus;
            if (status != null)
            {
                this.statusLabel.Text = status.StatusMessage.ToString();
            }
        }

        /// <summary>
        /// Sizes the columns in the ListView just the way I like 'em. :)
        /// </summary>
        private void SizeListViewColumns()
        {
            this.duplicateListView.BeginUpdate();

            if (this.duplicateListView.Items.Count == 0)
            {
                foreach (ColumnHeader clm in this.duplicateListView.Columns)
                {
                    clm.AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize);
                }
            }
            else
            {
                foreach (ColumnHeader clm in this.duplicateListView.Columns)
                {
                    clm.AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
                }
            }

            this.duplicateListView.EndUpdate();
        }

        /// <summary>
        /// Handles the Load event for the form.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// Data specific to the event.
        /// </param>
        private void MainFormLoad(object sender, EventArgs e)
        {
            this.UpdateFileCounts();
        }

        /// <summary>
        /// Handles the Click event for the Exit menu item.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// Data specific to the event.
        /// </param>
        private void ClickExitToolStripMenuItem(object sender, EventArgs e)
        {
            this.Close();
        }
        
        /// <summary>
        /// Handles the Click event for the "export to XML" menu item.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// Data specific to the event.
        /// </param>
        private void ClickExportToXMLToolStripMenuItem(object sender, EventArgs e)
        {
            if (this.db != null)
            {
                PatrickSeymour.FileDialogFilterBuilder filterBuilder = new PatrickSeymour.FileDialogFilterBuilder();
                filterBuilder.IncludeAllFiles = true;
                filterBuilder.AddExtension("xml");
                filterBuilder.ShowExtensions = true;

                SaveFileDialog sfd = new SaveFileDialog();
                sfd.CheckPathExists = true;
                sfd.DefaultExt = "xml";
                sfd.Filter = filterBuilder.FilterString;
                sfd.FilterIndex = 0;
                sfd.OverwritePrompt = true;
                sfd.Title = "Export SFT File Information to";
                if (sfd.ShowDialog(this) == System.Windows.Forms.DialogResult.OK)
                {
                    this.db.WriteXmlFile(sfd.FileName);
                    StringBuilder message = new StringBuilder("SFT data has been exported to");
                    message.Append(System.Environment.NewLine);
                    message.Append("\"");
                    message.Append(sfd.FileName);
                    message.Append(".\"");
                    MessageBox.Show(this, message.ToString(), "Duplicate Root Detector", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
                }
            }
        }

        /// <summary>
        /// Handles the Click event for the Export menu.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// Data specific to the event.
        /// </param>
        private void ClickExportToolStripMenuItem(object sender, EventArgs e)
        {
            // Only enable certain menu items if the database is not null.
            this.allDataToXMLToolStripMenuItem.Enabled = this.db != null;
            this.rootFolderListToHTMLToolStripMenuItem.Enabled = this.db != null;
        }

        /// <summary>
        /// Handles the Click event for the "export to HTML" menu item.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// Data specific to the event.
        /// </param>
        private void ClickExportToHTMLToolStripMenuItem(object sender, EventArgs e)
        {
            if (this.db != null)
            {
                // Write the database's data to an XML file temporarily.
                string xmlFilePath = System.IO.Path.GetTempFileName();
                this.db.WriteXmlFile(xmlFilePath);

                // Let the user transform the XML data using a stylesheet.
                XslTransformForm transForm = new XslTransformForm(xmlFilePath);
                transForm.ShowDialog(this);

                // Try to delete the temporary XML file. Just eat the error if it fails.
                try
                {
                    System.IO.FileInfo fi = new System.IO.FileInfo(xmlFilePath);
                    fi.Attributes = System.IO.FileAttributes.Normal;
                    fi.Delete();
                }
                catch (Exception)
                {
                }
            }
        }

        /// <summary>
        /// Handles the Closing event for the form.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// Data specific to the event.
        /// </param>
        private void FormClosingEventHandler(object sender, FormClosingEventArgs e)
        {
            // Save the form size and location values.
            DuplicateRootDetector.Properties.Settings.Default.mainFormLocation = this.Location;
            DuplicateRootDetector.Properties.Settings.Default.mainFormSize = this.Size;
            DuplicateRootDetector.Properties.Settings.Default.Save();
        }
    }
}